package cn.com.jonpad.config;

import cn.com.jonpad.config.filter.AfterCsrfFilter;
import cn.com.jonpad.config.filter.BeforeLoginFilter;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;

/**
 * @author jon75
 */
@EnableWebSecurity
@AllArgsConstructor
public class WebSecurityConfig  extends WebSecurityConfigurerAdapter {
	private AnyUserDetailsService anyUserDetailsService;
	/**
	 * 注：显示配置得登陆路径后将不会使用Security默认得登陆页面
	 *
	 * 通常，子类不能通过调用 super 来调用此方法，因为它可能会覆盖其配置。 默认配置为：
	 *    http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
	 *
	 * 匹配 "/" 路径，不需要权限即可访问
	 * 匹配 "/user" 及其以下所有路径，都需要 "USER" 权限
	 * 登录地址为 "/login"，登录成功默认跳转到页面 "/user"
	 * 退出登录的地址为 "/logout"，退出成功后跳转到页面 "/login"
	 * 默认启用 CSRF
	 *
	 * Spring Security 默认的过滤器链
	 * 官网位置：http://docs.spring.io/spring-security/site/docs/5.0.0.M1/reference/htmlsingle/#ns-custom-filters
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.cors().and()
				.authorizeRequests()
				.antMatchers("/").permitAll()
				.antMatchers("/user/**").hasRole("USER")
				.and()
				.formLogin().loginPage("/login").defaultSuccessUrl("/user")
				.and()
				.logout().logoutUrl("/logout").logoutSuccessUrl("/login");
		// 在 UsernamePasswordAuthenticationFilter 前添加 BeforeLoginFilter
		http.addFilterBefore(new BeforeLoginFilter(), UsernamePasswordAuthenticationFilter.class);

		// 在 CsrfFilter 后添加 AfterCsrfFilter
		http.addFilterAfter(new AfterCsrfFilter(), CsrfFilter.class);
	}

	/**
	 * 添加 UserDetailsService， 实现自定义登录校验
	 */
	@Override
	protected void configure(AuthenticationManagerBuilder builder) throws Exception{
		builder.userDetailsService(anyUserDetailsService)
				.passwordEncoder(passwordEncoder());
	}

	/**
	 * 密码加密
	 * BCryptPasswordEncoder相关知识：
	 *
	 * 用户表的密码通常使用MD5等不可逆算法加密后存储，为防止彩虹表破解更会先使用一个特定的字符串（如域名）加密，然后再使用一个随机的salt（盐值）加密。
	 *
	 * 特定字符串是程序代码中固定的，salt是每个密码单独随机，一般给用户表加一个字段单独存储，比较麻烦。
	 *
	 * BCrypt算法将salt随机并混入最终加密后的密码，验证时也无需单独提供之前的salt，从而无需单独处理salt问题。
	 *
	 * 补充说明：即使不同的用户注册时输入相同的密码，存入数据库的密文密码也会不同。
	 * @return
	 */
	@Bean
	public BCryptPasswordEncoder passwordEncoder(){
		return new  BCryptPasswordEncoder();
	}
}
